#pragma once
#include <cassert>
#include <vector>
#include <atomic>
#include <cstddef>

namespace adalog
{
    class Buffer
    {
    public:
        Buffer();
        /**
         * @param data 指向数据的第一个字节的指针
         * @param len 数据的字节数，即数据的长度
         */
        void Push(const char* data, size_t len);
        /**
         * @param len 要读取的数据的长度，若没有那么多可读取数据，则会发出警告 
         * @return 返回指向数据首字节的char指针，需要注意读取之后若下一次需要获取下一个数据则需要调用一次moveReadPos()
         */
        char* ReadBegin(size_t len);
        /**
         * @return 缓冲区为空(可读取数据为空)则为true；否则返回false
         */
        bool IsEmpty() const;
        /**
         * 将本缓冲区和buf的缓冲区做交换，并不会复制buf的元素，而是交换二者的一些指针数据
         * @param buf 要交换的缓冲区
         */
        void Swap(Buffer& buf);
        /**
         * @return 返回缓冲区可写的空间大小
         */
        size_t WritableSize() const;
        /**
         * @return 返回缓冲区中可读的空间大小
         */
        size_t ReadableSize() const;
        /**
         * @return 返回指向缓冲区可读的首字节的const char指针
         */
        const char* Begin() const;
        /**
         * 将写指针向后移len个字节
         * @param len 移动的字节数
         */
        void MoveWritePos(size_t len);
        /**
         * 将读指针向后移len个字节
         * @param len 移动的字节数
         */
        void MoveReadPos(size_t len);
        /**
         * 重置缓冲区的读、写指针，使得之前读取的区域可以被再次利用，否则会缓冲区会无限扩容
         */
        void Reset();

    protected:
        /**
         * 当可写的空间大小小于len的时候，进行扩容。若不想要Buffer在Push的过程中扩容，就需要Push方法的调用者自行确保可写的空间大小大于len
         * @param len 
         */
        void ToBeEnouth(size_t len);

    private:
        std::vector<char> buffer_;
        std::atomic<size_t> write_pos_;
        size_t read_pos_;
        // 缓冲区大小增长的阈值，超过该阈值线性增长；低于该阈值指数增长
        size_t threshold_;
        // 线性增长的大小
        size_t linear_growth_;
    };

} // namespace adalog